package mymap.web.builder;

import geo.google.GeoAddressStandardizer;
import geo.google.GeoException;
import geo.google.datamodel.GeoAddress;
import geo.google.mapping.XmlMappingFunctor;
import geo.google.mapping.XmlToAddressFunctor;
import geo.google.utils.XmlUtils;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import mymap.utils.JsfUtil;
import mymap.utils.Pair;
import mymap.utils.image.CropImage;
import mymap.utils.image.ImageUtils;
import mymap.utils.map.GMapUtil;
import mymap.utils.map.GoogleMaps;
import mymap.utils.zip.JarUtils;
import mymap.web.beans.LatLonBox;
import mymap.web.beans.MapBean;
import mymap.web.beans.Quadro;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;

public class MapBuilder {

	GoogleMaps gmap = new GoogleMaps();
	private String MAPTYPE;
	private String IMAGE_DIR;

	private int WIDTH = Quadro.SIZE;
	private int HEIGHT = Quadro.SIZE;
	
	//tamanho do quadro q sera baixado do google
	private int FAKE_IMAGE_SIZE = 512;

	private static final String FILE_TYPE = "png";

	public MapBuilder() {
		MAPTYPE = "mobile";
		IMAGE_DIR = JsfUtil.getWebPath() + "mapas/downloads/";
	}

	public MapBuilder(String mapType, String imageDir) {
		MAPTYPE = mapType;
		IMAGE_DIR = imageDir;
	}

	public Image getImage(MapBean bean, Double lng, Double lat)
			throws IOException {
		return gmap.getMap(WIDTH, HEIGHT, lng, lat, bean.getZoom(), MAPTYPE);
	}

	
	/**
	 * Salva a imagem no diretorio padrao
	 * 
	 * @param image
	 * @param fileName
	 * @return path da imagem
	 * @throws IOException
	 */
	private File storeImage(Image image, String dir, String fileName) throws IOException {
		return ImageUtils.storeImage(image, IMAGE_DIR+dir + "/" , fileName, FILE_TYPE);
		//return storeImage(image, IMAGE_DIR, fileName);
	}
		

	/**
	 * Retorna um HashMap com uma classe GeoAddress e LatLonBox
	 * @param endereco
	 * @return
	 * @throws GeoException
	 */
	public Pair<GeoAddress, LatLonBox> getGeoAddress(String endereco)
			throws GeoException {

		GeoAddressStandardizer st = new GeoAddressStandardizer(GoogleMaps.KEY);
		st.setRateLimitInterval(3000);
		// List<GeoAddress> addr = st.standardizeToGeoAddresses(endereco);

		Pair<GeoAddress, LatLonBox> geoPair = st.standardize(endereco,

		new XmlMappingFunctor<Pair<GeoAddress, LatLonBox>>() {
			public Pair<GeoAddress, LatLonBox> execute(String xml)
					throws GeoException {
				Document doc;
				try {
					//parseia o XML
					doc = XmlUtils.parse(xml);

					NamedNodeMap attributesBox = doc.getElementsByTagName("LatLonBox").item(0).getAttributes();
					
					// popula LatLonBox
					LatLonBox latLonBox = new LatLonBox();
					latLonBox.setEast(attributesBox.getNamedItem("east").getNodeValue());
					latLonBox.setWest(attributesBox.getNamedItem("west").getNodeValue());
					latLonBox.setNorth(attributesBox.getNamedItem("north").getNodeValue());
					latLonBox.setSouth(attributesBox.getNamedItem("south").getNodeValue());

					XmlToAddressFunctor xmlToAddress = XmlToAddressFunctor.getInstance();
					GeoAddress ga = xmlToAddress.execute(xml).get(0);

					return new Pair<GeoAddress, LatLonBox>(ga, latLonBox);
				} catch (Exception e) {
					throw new GeoException("Não foi possível parsear o XML: ",e);
				}
			}
		});

		return geoPair;
	}
	
		public GeoAddress getSimpleGeoAddress(String endereco) throws GeoException {
	
		GeoAddressStandardizer st = new GeoAddressStandardizer(GoogleMaps.KEY);
		st.setRateLimitInterval(3000);
		List<GeoAddress> addr = st.standardizeToGeoAddresses(endereco);

		if (addr == null || addr.isEmpty())
			throw new GeoException("Nada encontrado");
			
		return addr.get(0);
	}
	
	
	public String gerarMapa(MapBean mapBean) throws Exception{
		
		//mapBean.getLatLonBox()
		
		Integer zoom = mapBean.getZoom();
		//LatLonBox latLonBox = mapBean.getLatLonBox();
		LatLonBox latLonBox = mapBean.getFakeLatLonBox();
		
		
		//quadros que seram gerados p/ rodar no celular
		int numQuadrosX = latLonBox.getWidth(zoom) / Quadro.SIZE;
		int numQuadrosY = latLonBox.getHeigt(zoom) / Quadro.SIZE;
		Quadro[][] matrixQuadroCel = new Quadro[numQuadrosX][numQuadrosY];
		

		//o numero das imagens que seram pegas pelo google map
		int numImagensX = latLonBox.getWidth(zoom) / FAKE_IMAGE_SIZE;
		int numImagensY = latLonBox.getHeigt(zoom) / FAKE_IMAGE_SIZE;
		Quadro[][] matrixImgsFromGoogle = new Quadro[numImagensX][numImagensY];
		
		
		
		//cria os quadros q irao para o celular
		for(int x=0; x < numQuadrosX; x++){
			for(int y=0; y < numQuadrosY; y++){
				Quadro q = GMapUtil.getQuadro(x, y, latLonBox.getNorth(), latLonBox.getWest(), zoom);
				System.out.println(q.toString());
				matrixQuadroCel[x][y] = q;
			}
		}
		
		//cria os 'quadros das imagens q virao do google'
		for(int x=0; x < numImagensX; x++){
			for(int y=0; y < numImagensY; y++){
				Quadro q = GMapUtil.getQuadro(x, y, latLonBox.getNorth(), latLonBox.getWest(), zoom);
				System.out.println("imagem: " + q.toString());
				matrixImgsFromGoogle[x][y] = q;
			}
		}
		
		
		
		String dirName = String.valueOf(new Date().getTime());

		int proporcao = FAKE_IMAGE_SIZE / 512;
		int index = 0;
		
		//utilizado para gerar o thumb do mapa inteiro
		Image[][] imagesToThumb = new Image[matrixQuadroCel.length][matrixQuadroCel[0].length];
		
		
		//pega as imagens do google e as 'quebra'
		for(int x=0; x < matrixQuadroCel.length; x+=proporcao){
			for(int y=0; y < matrixQuadroCel[x].length; y+=proporcao){
				
				Quadro quadro = matrixQuadroCel[x / proporcao][y / proporcao];
				
				Image image = getImage(mapBean, quadro.getLongCenter(), quadro.getLatCenter());

				Image[][] images = CropImage.toMatrixImage(image, proporcao);

				for (int x1=0; x1 < images.length; x1++){
					for (int y1=0; y1 < images[x1].length; y1++){
						Quadro q = matrixQuadroCel[x+x1][y+y1];
						storeImage(images[x1][y1], dirName,  q.getNomeImagem());
						//cria as partes dos thumbs
						imagesToThumb[x+x1][y+y1] = images[x1][y1]; //ImageUtils.joinImages(images, 128);
					}
				}
				
				System.out.println("Imagem do Google Maps: "+quadro.getNomeImagem()+" [Lat: "+quadro.getLatCenter()
						+ " - Long: " + quadro.getLongCenter() + "]");
			}
			index+=2;
		}
		
		//cria miniatura do mapa inteiro
		BufferedImage tumb = ImageUtils.joinImages(imagesToThumb, 128);
		//storeImage(tumb, dirName,  "thumbBig");
		tumb = ImageUtils.resize(tumb, 150);
		storeImage(tumb, dirName,  "thumb.png");
		
		String jarName = mapBean.getGeoAddress().getAddressLine();
		jarName = jarName.replaceAll(" ", "_").replaceAll(",", "");
		jarName = dirName.substring(0, 9)+"-"+jarName;
		return JarUtils.gerarJar(dirName, jarName);
		
	}
	
	

}